﻿// <copyright file="MouseHelper.cs" company="Infusion Development">Copyright (c) 2009 All Right Reserved</copyright>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

namespace MvvmFx.Common.Helpers
{
    /// <summary>
    /// The MouseHelper class.
    /// </summary>
    public static class MouseHelper
    {
        #region Double-Click Support

        /// <summary>
        /// Constant double click interval field.
        /// </summary>
        private const int DOUBLE_CLICK_INTERVAL = 200;

        /// <summary>
        /// Register an attached dependeny property DoubleClickTimerProperty.
        /// </summary>
        private static readonly DependencyProperty DoubleClickTimerProperty = DependencyProperty.RegisterAttached("DoubleClickTimer", typeof(DispatcherTimer), typeof(UIElement), null);
     
        /// <summary>
        /// Register an attached dependeny property DoubleClickHandlersProperty.
        /// </summary>
        private static readonly DependencyProperty DoubleClickHandlersProperty = DependencyProperty.RegisterAttached("DoubleClickHandlers", typeof(List<MouseButtonEventHandler>), typeof(UIElement), null);

        /// <summary>
        /// Register an attached dependeny property DoubleClickPositionProperty.
        /// </summary>
        private static readonly DependencyProperty DoubleClickPositionProperty = DependencyProperty.RegisterAttached("DoubleClickPosition", typeof(Point), typeof(UIElement), null);

        /// <summary>
        /// Adds a double click event handler.
        /// </summary>
        /// <param name="element">The Element to listen for double clicks on.</param>
        /// <param name="handler">The handler.</param>
        public static void AddDoubleClick(this UIElement element, MouseButtonEventHandler handler)
        {
            element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
            List<MouseButtonEventHandler> handlers = element.GetValue(DoubleClickHandlersProperty) as List<MouseButtonEventHandler>;
            if (handlers == null)
            {
                handlers = new List<MouseButtonEventHandler>();
                element.SetValue(DoubleClickHandlersProperty, handlers);
            }

            handlers.Add(handler);
        }

        /// <summary>
        /// Removes a double click event handler.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="handler">The handler.</param>
        public static void RemoveDoubleClick(this UIElement element, MouseButtonEventHandler handler)
        {
            element.MouseLeftButtonDown -= Element_MouseLeftButtonDown;
            List<MouseButtonEventHandler> handlers = element.GetValue(DoubleClickHandlersProperty) as List<MouseButtonEventHandler>;
            if (handlers != null)
            {
                handlers.Remove(handler);
                if (handlers.Count == 0)
                {
                    element.ClearValue(DoubleClickHandlersProperty);
                }
            }
        }

        /// <summary>
        /// Occurs when the <see cref="UIElement.MouseLeftButtonDown"/> changes. Notify if a double 
        /// click handler should be fired. 
        /// </summary>
        /// <param name="sender">The <see cref="object"/> object that fired the event.</param>
        /// <param name="e">The <see cref="MouseButtonEventArgs"/> event fired.</param>
        private static void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            UIElement element = sender as UIElement;
            Point position = e.GetPosition(element);
            DispatcherTimer timer = element.GetValue(DoubleClickTimerProperty) as DispatcherTimer;
            
            // Double click
            if (timer != null) 
            {
                timer.Stop();
                Point oldPosition = (Point)element.GetValue(DoubleClickPositionProperty);
                element.ClearValue(DoubleClickTimerProperty);
                element.ClearValue(DoubleClickPositionProperty);

                // Mouse didn't move => Valid double click
                if (Math.Abs(oldPosition.X - position.X) < 1 && Math.Abs(oldPosition.Y - position.Y) < 1) 
                {
                    List<MouseButtonEventHandler> handlers = element.GetValue(DoubleClickHandlersProperty) as List<MouseButtonEventHandler>;
                    if (handlers != null)
                    {
                        foreach (MouseButtonEventHandler handler in handlers)
                        {
                            handler(sender, e);
                        }
                    }

                    return;
                }
            }

            //First click or mouse moved. Start a new timer
            timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(DOUBLE_CLICK_INTERVAL) };
            timer.Tick += ((s, args) =>
                               {  //DblClick timed out
                                   DispatcherTimer dispatcherTimer = s as DispatcherTimer;
                                   if(dispatcherTimer != null)
                                   {
                                    dispatcherTimer.Stop();    
                                   }

                                   element.ClearValue(DoubleClickTimerProperty); //clear timer
                                   element.ClearValue(DoubleClickPositionProperty); //clear first click position
                               });
            element.SetValue(DoubleClickTimerProperty, timer);
            element.SetValue(DoubleClickPositionProperty, position);
            timer.Start();
        }

        #endregion
    }
}